home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Language/OS - Multiplatform Resource Library
/
LANGUAGE OS.iso
/
scheme
/
boxer
/
boxer.lha
/
evprim.lisp
< prev
next >
Wrap
Text File
|
1993-07-17
|
28KB
|
747 lines
;;;-*- mode:lisp;syntax :zetalisp; package: Boxer; fonts: cptfont, cptfontb -*-
#|
Copyright 1985 Massachusetts Institute of Technology
Permission to use, copy, modify, distribute, and sell this software
and its documentation for any purpose is hereby granted without fee,
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in
supporting documentation, and that the name of M.I.T. not be used in
advertising or publicity pertaining to distribution of the software
without specific, written prior permission. M.I.T. makes no
representations about the suitability of this software for any
purpose. It is provided "as is" without express or implied warranty.
+-Data--+
This file is part of the | BOXER | system
+-------+
This file defines the internal representation for Boxes used by the Evaluator.
We use a different represention for boxes in the Evaluator for the following reasons:
The Evaluator will have to create a large number of Boxes during the course
of a normal execution. It is to our advantage to have small objects for boxes
for both speed (CONSing time) and space considerations. General purpose Boxes
are not small and they contain a lot of irrelevant(to the Evaluator) information.
Having a different representation for Boxes also allows us to keep the format of
the Box in a form that is partially pre-digested for the Evaluator--saving us
a few more cycles.
The Evaluator representation of a Box is known as an Evbox for Evaluator Box.
Evboxes have the following properties:
Its contents. The representation of the contents should be in a form that the
Evaluator can deal with easily(what the reader spits out perhaps). Spacing information
is also preserved.
EvPORTS will have a pointer to the target of the port
The name ?
The Local Variable Bindings;
This code is descended from similiar "Pre-box" code written by Mike Eisenberg
The Actual Definitions for the structures used here are in the file EVDEFS
|#
;;;; Some predicates
(DEFUN BLANK-EV-ROW? (EV-ROW)
(OR (NULL EV-ROW)
(NOT (DOLIST (ENTRY EV-ROW) (UNLESS (SPACES? ENTRY) (RETURN T))))))
(DEFUN EMPTY-EVROW? (EVROW)
(OR (NULL EVROW) (NULL (EVROW-ENTRIES EVROW))))
(DEFF BLANK-EV-ROW? #'EMPTY-EVROW?)
(COMPILER:MAKE-OBSOLETE BLANK-EV-ROW? "Use EMPTY-EVROW? instead.")
(DEFUN EMPTY-EVBOX? (EVBOX)
(LET ((ROWS (EVBOX-ROWS EVBOX)))
(OR (NULL ROWS)
(NOT (DOLIST (ROW ROWS) (UNLESS (EMPTY-EVROW? ROW) (RETURN T)))))))
;;; HOW to translate Boxes (EDITOR instances) into evaluator objects (EvBoxes) and vice versa
;; make an empty EvBOX
(DEFUN MAKE-EMPTY-EVBOX (&OPTIONAL (TYPE 'DATA-BOX))
(SELECTQ TYPE
((DATA-BOX :DATA-BOX) (MAKE-EVDATA ROWS `(,(MAKE-EVROW))))
((DOIT-BOX :DOIT-BOX) (MAKE-EVDOIT ROWS `(,(MAKE-EVROW))))
((PORT-BOX :PORT-BOX) (MAKE-EVPORT)) ;will we EVER need to do this ???
(OTHERWISE (FERROR "Don't know how to make an empty EvBox of type ~A" TYPE))))
(DEFUN MAKE-EVROW-FROM-ROW (ROW)
(MAKE-EVROW ENTRIES (TELL ROW :ENTRIES) ITEMS (TELL ROW :ITEMS)))
(DEFUN MAKE-EVROWS-FROM-BOX (BOX)
(MAPCAR #'MAKE-EVROW-FROM-ROW (TELL BOX :ROWS)))
;; need to flatten out exports here
(defun copy-local-bindings (box)
(let ((exporting-marker-entry (assq *exporting-box-marker*
(TELL BOX :GET-STATIC-VARIABLES-ALIST))))
(if (null exporting-marker-entry)
(COPYLIST (TELL BOX :GET-STATIC-VARIABLES-ALIST))
(nconc (remq exporting-marker-entry (TELL BOX :GET-STATIC-VARIABLES-ALIST))
(copy-local-bindings (cdr exporting-marker-entry))))))
(DEFUN GET-NAME-FOR-EVBOX (BOX)
(IF (STRING-EQUAL (TELL BOX :NAME) "Un-named") NIL (TELL BOX :NAME)))
;;; Shallow copying for the evaluator
(DEFUN COPY-EVBOX (EVBOX)
"Does a top level copy. Elements are NOT copied"
(SELECTQ (TYPEP EVBOX)
((EVDOIT) (COPY-EVDOIT EVBOX))
((EVDATA) (COPY-EVDATA EVBOX))
((EVPORT) (COPY-EVPORT EVBOX))
(T (FERROR "Dont' know how to copy ~A" EVBOX))))
;;; copying no longer flattens boxes down into numbers because the number-box? check
;;; is costing us about 2500 microseconds PER COPY (and we are copying EVERYWHERE...)
;;; This is as opposed to the fact that CONSing up an evbox takes about 380 microseconds
;;; and getting the template for some function that REALLY wants a number (not that many due
;;; to the existence of data-box arithmetic) like a turtle function doesn't cost us anything
;;; cause we got to look for it anyway
(DEFUN SHALLOW-COPY-FOR-EVALUATOR (THING)
(COND ((NOT (OR (EVAL-BOX? THING) (EVAL-PORT? THING))) THING) ;non-boxes
((OR (GRAPHICS-BOX? THING) (GRAPHICS-DATA-BOX? THING) (SPRITE-BOX? THING))
;; a crock to make graphics work
THING)
((BOX? THING) (MAKE-TOP-LEVEL-EVBOX-FROM-BOX THING)) ;editor boxes ev boxes
(T (COPY-EVBOX THING))))
;;; This is the same as the above Except that numbers are boxifed instead of number boxes
;;; being flattened out into numbers
(DEFUN SHALLOW-COPY-FOR-ARGLIST (THING)
(COND ((NUMBERP THING) (MAKE-EVDATA ROWS `(,(MAKE-EVROW-FROM-ENTRY THING)))) ;numbers box
((NOT (OR (EVAL-BOX? THING) (EVAL-PORT? THING))) THING) ;anything but boxes
((OR (GRAPHICS-BOX? THING) (GRAPHICS-DATA-BOX? THING) (SPRITE-BOX? THING))
;; a crock to make graphics work
THING)
((BOX? THING) (MAKE-TOP-LEVEL-EVBOX-FROM-BOX THING)) ;editor boxes
(T (COPY-EVBOX THING))))
;;; It is useful to set *EVALUATOR-BOX-COPYING-FUNCTION* to this for metering purposes
(DEFUN NO-COPY (BOX)
BOX)
;; this is the top level function to be called to get an EvBOX
(DEFUN MAKE-TOP-LEVEL-EVBOX-FROM-BOX (BOX)
(MAKE-EVBOX-FROM-BOX BOX NIL))
(DEFUN MAKE-EVBOX-FROM-BOX (BOX &OPTIONAL (NAME (GET-NAME-FOR-EVBOX BOX)))
(CHECK-BOX-ARG BOX) ;we can take this out later for speed
(SELECTQ (TYPEP BOX)
((DOIT-BOX :DOIT-BOX) (MAKE-EVDOIT ROWS (MAKE-EVROWS-FROM-BOX BOX)
BINDINGS (COPY-LOCAL-BINDINGS BOX)
NAME NAME))
((DATA-BOX :DATA-BOX) (MAKE-EVDATA ROWS (MAKE-EVROWS-FROM-BOX BOX)
BINDINGS (COPY-LOCAL-BINDINGS BOX)
NAME NAME))
((PORT-BOX :PORT-BOX) (MAKE-EVPORT TARGET (TELL BOX :PORTS)
NAME NAME))
(OTHERWISE (FERROR "Don't know how to make an Evbox from ~A" BOX))))
(DEFUN MAKE-BOX-FROM-EVBOX (EVBOX)
(SELECTQ (TYPEP EVBOX)
((EVDATA EVDOIT)
(let ((new-box (MAKE-BOX (EVBOX-ROW-ITEMS EVBOX) :DATA-BOX (EVBOX-NAME EVBOX))))
(let ((ll (get-evbox-local-library evbox)))
(when (not (null ll))
(send new-box :set-local-library ll)
(send new-box :add-static-variable-pair *exporting-box-marker* ll)
(send ll :export-all-variables)))
new-box))
((EVPORT) (PORT-TO-INTERNAL (GET-PORT-TARGET EVBOX)))))
;;;; Stream interface
(DEFUN MAKE-ROW-STREAM-FROM-EVROW (EVROW)
(MAKE-ROW-STREAM `(:ROW . ,(EVROW-ITEMS EVROW))))
(DEFUN MAKE-EVDATA-STREAM (EVDATA)
(MAKE-BOX-STREAM `(:BOX
(:TYPE :DATA-BOX :DISPLAY-STYLE-LIST (:NORMAL NIL NIL)
:NAME ,(EVBOX-NAME EVDATA))
,@(LOOP FOR ROW IN (EVBOX-ROWS EVDATA)
COLLECT (MAKE-ROW-STREAM-FROM-EVROW ROW)))))
(DEFUN MAKE-EVDOIT-STREAM (EVDATA)
(MAKE-BOX-STREAM `(:BOX
(:TYPE :DOIT-BOX :DISPLAY-STYLE-LIST (:NORMAL NIL NIL)
:NAME ,(EVBOX-NAME EVDATA))
,@(LOOP FOR ROW IN (EVBOX-ROWS EVDATA)
COLLECT (MAKE-ROW-STREAM-FROM-EVROW ROW)))))
;; this needs to handle targets which are evboxes (2 cases here: evbox is(not) also returned)
(DEFUN MAKE-EVPORT-STREAM (EVPORT)
(MAKE-BOX-STREAM (PORT-TO-INTERNAL (EVPORT-TARGET EVPORT))))
(DEFPROP EVDATA MAKE-EVDATA-STREAM :MAKE-BOXER-STREAM)
(DEFPROP EVDOIT MAKE-EVDOIT-STREAM :MAKE-BOXER-STREAM)
(DEFPROP EVPORT MAKE-EVPORT-STREAM :MAKE-BOXER-STREAM)
;;; This is used to convert the result of BOXER-READ into something
;;; the evaluator can deal with
(DEFUN PARSE-LIST-FOR-EVAL (LIST)
(LOOP FOR ELEMENT IN LIST
UNTIL (COMMENT-CHA? ELEMENT)
UNLESS (SPACES? ELEMENT)
COLLECT (COND ((OR (LABEL-PAIR? ELEMENT) (EVAL-IT-TOKEN? ELEMENT)
(UNBOX-TOKEN? ELEMENT))
ELEMENT)
((LISTP ELEMENT) (PARSE-LIST-FOR-EVAL ELEMENT))
(T ELEMENT))))
(DEFUN TOTALLY-DEBLANK (EVROW)
(REM #'(LAMBDA (BLKSYM ENTRY)(AND (LISTP ENTRY)(EQ (CAR ENTRY) BLKSYM)))
*SPACING-INFO-SYMBOL* EVROW))
(COMPILER:MAKE-OBSOLETE TOTALLY-DEBLANK
"You probably want to be using PARSE-LIST-FOR-EVAL instead. ")
(DEFUN REMOVE-SPACES-FROM-LEFT (LIST)
(MEM #'(LAMBDA (IGNORE X) (NOT (SPACES? X))) 'IGNORE LIST))
(DEFUN LEFT-JUSTIFY (EVROW)
(SETF (EVROW-ITEMS EVROW) (REMOVE-SPACES-FROM-LEFT (EVROW-ITEMS EVROW))))
(DEFUN ADD-SPACES-TO-RIGHT (LIST SPACES)
(APPEND LIST (NCONS (MAKE-SPACES SPACES))))
;;;; Evaluator Interface
(DEFUN EVBOX-HAS-INPUTS? (EVBOX)
(MEMQ (GET-FIRST-ELEMENT-IN-EVROW (GET-FIRST-ROW-IN-EVBOX EVBOX)) *SYMBOLS-FOR-INPUT-LINE*))
(DEFUN GET-EVBOX-ROWS-FOR-EVAL (EVBOX)
(IF (EVBOX-HAS-INPUTS? EVBOX)
(CDR (EVBOX-ROW-ENTRIES EVBOX))
(EVBOX-ROW-ENTRIES EVBOX)))
;;;; Getting useful info ABOUT Evboxes
(DEFUN EVROW-LENGTH-IN-ELEMENTS (EVROW)
(LENGTH (EVROW-ENTRIES EVROW)))
(DEFF EV-ROW-LENGTH-IN-ELEMENTS #'EVROW-LENGTH-IN-ELEMENTS)
(COMPILER:MAKE-OBSOLETE EV-ROW-LENGTH-IN-ELEMENTS "Use EVROW-LENGTH-IN-ELEMENTS instead. ")
(DEFSUBST EVBOX-LENGTH-IN-ROWS (EVBOX)
(LENGTH (EVBOX-ROWS EVBOX)))
(DEFUN EVBOX-LENGTH-IN-ELEMENTS (EVBOX)
(LOOP FOR ROW IN (EVBOX-ROWS EVBOX)
SUMMING (EVROW-LENGTH-IN-ELEMENTS ROW)))
(DEFUN CHA-LENGTH-OF-EVROW-ITEM (ITEM)
(COND ((SPACES? ITEM) (GET-SPACES ITEM))
((OR (EVBOX? ITEM) (BOX? ITEM) (EVPORT? ITEM)) 1)
((NUMBERP ITEM) (STRING-LENGTH (FORMAT NIL "~A" ITEM))) ;loses on *NOPOINT (sometimes)
((LABEL-PAIR? ITEM) (+ (STRING-LENGTH (LABEL-PAIR-LABEL ITEM))
(STRING-LENGTH (LABEL-PAIR-ELEMENT ITEM)) 1))
((UNBOX-TOKEN? ITEM) (1+ (CHA-LENGTH-OF-EVROW-ITEM (UNBOX-TOKEN-ELEMENT ITEM))))
((EVAL-IT-TOKEN? ITEM) (1+ (CHA-LENGTH-OF-EVROW-ITEM (EVAL-IT-TOKEN-ELEMENT ITEM))))
(T (STRING-LENGTH ITEM))))
(DEFSUBST ITEM-LIST-LENGTH-IN-CHAS (LIST)
(LOOP FOR ITEM IN LIST SUMMING (CHA-LENGTH-OF-EVROW-ITEM ITEM)))
(DEFUN EVROW-LENGTH-IN-CHAS (EVROW)
(ITEM-LIST-LENGTH-IN-CHAS (EVROW-ITEMS EVROW)))
(DEFF EV-ROW-LENGTH-IN-CHAS #'EVROW-LENGTH-IN-CHAS)
(COMPILER:MAKE-OBSOLETE EV-ROW-LENGTH-IN-CHAS "Use EVROW-LENGTH-IN-CHAS instead. ")
(DEFUN EVROWS-MAX-LENGTH-IN-CHAS (ROWS)
(LOOP FOR ROW IN ROWS
MAXIMIZE (EVROW-LENGTH-IN-CHAS ROW)))
(DEFUN EVBOX-MAX-LENGTH-IN-CHAS (EVBOX)
(EVROWS-MAX-LENGTH-IN-CHAS (EVBOX-ROWS EVBOX)))
;;; Stringifying
(DEFSUBST MAKE-BLANK-STRING (LENGTH)
(STRING (MAKE-ARRAY LENGTH ':TYPE 'ART-STRING :INITIAL-VALUE #\SPACE)))
(DEFUN STRINGIFY (ITEM)
(COND ((SPACES? ITEM) (MAKE-BLANK-STRING (GET-SPACES ITEM)))
((EVAL-BOX? ITEM) "[]")
((NUMBERP ITEM) (FORMAT NIL "~A" ITEM))
((LABEL-PAIR? ITEM)
(FORMAT NIL "~A:~A" (LABEL-PAIR-LABEL ITEM) (LABEL-PAIR-ELEMENT ITEM)))
((UNBOX-TOKEN? ITEM) (FORMAT NIL "@~A" (UNBOX-TOKEN-ELEMENT ITEM)))
((EVAL-IT-TOKEN? ITEM) (FORMAT NIL "!~A" (EVAL-IT-TOKEN-ELEMENT ITEM)))
((AND (SYMBOLP ITEM) (GET ITEM 'CONVERTED-CHARACTER))
(FORMAT NIL "~C" (GET ITEM 'CONVERTED-CHARACTER)))
((LISTP ITEM)
(LET ((RETURN-STRING ""))
(DOLIST (I ITEM)
(SETQ RETURN-STRING (STRING-APPEND RETURN-STRING (STRINGIFY I))))
RETURN-STRING))
(T (STRING ITEM))))
(DEFUN EVROW-TEXT-STRING (ROW)
(LET ((RETURN-STRING ""))
(DOLIST (ITEM (EVROW-ITEMS ROW))
(SETQ RETURN-STRING (STRING-APPEND RETURN-STRING (STRINGIFY ITEM))))
RETURN-STRING))
(DEFUN EVBOX-TEXT-STRING (BOX)
(DO* ((ROWS (EVBOX-ROWS BOX) (CDR ROWS))
(ROW (CAR ROWS))
(STRING ""))
((NULL ROWS) STRING)
(SETQ STRING (STRING-APPEND STRING (EVROW-TEXT-STRING ROW)))
(UNLESS (EQ ROW (CAR ROWS))
(SETQ STRING (STRING-APPEND STRING (STRING #\CR))))))
;;;; Prebox selectors. Return NIL when the element isn't there
;;;; Maybe these should all be SUBSTs ??
(DEFSUBST GET-NTH-ROW-IN-EVBOX (N EVBOX)
(NTH N (EVBOX-ROWS EVBOX)))
(DEFSUBST GET-FIRST-ROW-IN-EVBOX (EVBOX)
(CAR (EVBOX-ROWS EVBOX)))
(DEFSUBST GET-FIRST-ELEMENT-IN-EVROW (EVROW)
(CAR (EVROW-ENTRIES EVROW)))
(DEFSUBST GET-LAST-ELEMENT-IN-EVROW (EVROW)
(CAR (LAST (EVROW-ENTRIES EVROW))))
;; 0 based
(DEFUN GET-NTH-ELEMENT-IN-EVROW (N EVROW)
(NTH N (EVROW-ENTRIES EVROW)))
(DEFSUBST REMOVE-FROM-LIST (N LIST)
(REMQ (NTH N LIST) LIST 1))
;;; These CONS up a new rows (no side effects !!!)
(DEFUN GET-BUTNTH-ELEMENT-IN-EVROW (N EVROW)
(LET* ((ENTRIES (EVROW-ENTRIES EVROW))
(ITEM (NTH N ENTRIES)))
(MAKE-EVROW ENTRIES (REMOVE-FROM-LIST N ENTRIES)
ITEMS (REMQ ITEM (EVROW-ITEMS EVROW) 1))))
(DEFSUBST GET-REST-ELEMENTS-IN-EVROW (EVROW)
(LET ((ENTRIES (EVROW-ENTRIES EVROW)))
(MAKE-EVROW ENTRIES (CDR ENTRIES)
ITEMS (REMOVE-SPACES-FROM-LEFT
(CDR (MEMQ (CAR ENTRIES) (EVROW-ITEMS EVROW)))))))
(DEFSUBST GET-BUTLAST-ELEMENTS-IN-EVROW (EVROW)
(LET ((ENTRIES (EVROW-ENTRIES EVROW)))
(MAKE-EVROW ENTRIES (BUTLAST ENTRIES)
ITEMS (REMQ (CAR (LAST ENTRIES)) (EVROW-ITEMS EVROW) 1))))
(DEFUN GET-EVBOX-ELEMENTS (EVBOX)
(LOOP FOR ROW-ENTRIES IN (EVBOX-ROW-ENTRIES EVBOX)
APPENDING ROW-ENTRIES))
;;; EvBox mutators
(DEFUN SET-NTH-ROW-IN-EVBOX (N BOX NEW-ROW)
(LET ((ROWS (EVBOX-ROWS BOX)))
(SETF (NTH N ROWS) NEW-ROW)))
(DEFPROP GET-NTH-ROW-IN-EVBOX
((GET-NTH-ROW-IN-EVBOX N EVBOX) SET-NTH-ROW-IN-EVBOX N EVBOX SI:VAL) SETF)
(DEFMACRO DELETE-NTH-ITEM-IN-EVROW (N EVROW)
`(LET ((ITEM (NTH ,N (EVROW-ENTRIES ,EVROW))))
(SPLICE-ITEM-OUT-OF-LIST (EVROW-ENTRIES ,EVROW) ITEM)
(SPLICE-ITEM-OUT-OF-LIST (EVROW-ITEMS ,EVROW) ITEM)))
(DEFMACRO INSERT-NTH-ITEM-IN-EVROW (N EVROW NEW-ITEM)
`(PROGN
(SPLICE-ITEM-INTO-LIST-AT (EVROW-ENTRIES ,EVROW) ,NEW-ITEM ,N)
(SPLICE-ITEM-INTO-LIST-AT (EVROW-ITEMS ,EVROW) ,NEW-ITEM
(OR (FIND-POSITION-IN-LIST (NTH ,N (EVROW-ENTRIES ,EVROW))
(EVROW-ITEMS ,EVROW))
(LENGTH (EVROW-ITEMS ,EVROW))))))
(DEFMACRO CHANGE-NTH-ITEM-IN-EVROW (N EVROW NEW-ITEM)
`(LET ((ITEM (NTH ,N (EVROW-ENTRIES ,EVROW))))
(SPLICE-ITEM-INTO-LIST (EVROW-ENTRIES ,EVROW) ,NEW-ITEM ITEM)
(SPLICE-ITEM-OUT-OF-LIST (EVROW-ENTRIES ,EVROW) ITEM)
(SPLICE-ITEM-INTO-LIST (EVROW-ITEMS ,EVROW) ,NEW-ITEM ITEM)
(SPLICE-ITEM-OUT-OF-LIST (EVROW-ITEMS ,EVROW) ITEM)))
;; CONSes up a new row, does NOT side effect
(DEFF REMOVE-NTH-ITEM-IN-EVROW #'GET-BUTNTH-ELEMENT-IN-EVROW)
;;;; Genericism...
;;; Predicates
(DEFSUBST EVAL-NAMED? (THING)
(OR (AND (BOX? THING) (NAME-ROW? (TELL THING :NAME-ROW)))
(AND (OR (EVBOX? THING) (EVPORT? THING)) (NOT-NULL (EVBOX-NAME THING)))))
(DEFUN EVAL-EMPTY? (BOX)
(NOT (DOLIST (ROW (GET-BOX-ROWS BOX)) (UNLESS (NULL ROW) (RETURN T)))))
(DEFSUBST NUMBER-BOX? (BOX)
(AND (= 1 (GET-BOX-LENGTH-IN-ELEMENTS BOX)) (NUMBERP (GET-FIRST-ELEMENT BOX))))
(DEFUN NUMBERIZE (THING)
(COND ((NUMBERP THING) THING)
((NUMBER-BOX? THING) (GET-FIRST-ELEMENT THING))
(T (FERROR "Can't convert ~A into a number. " THING))))
(DEFUN ELEMENT-EQUAL? (E1 E2)
(cond ((EQ (TOKEN-TYPE (ROW-ENTRY-ELEMENT E1)) (TOKEN-TYPE (ROW-ENTRY-ELEMENT E2)))
(COND ((EVAL-BOX? E1) (BOX-EQUAL? E1 E2))
((NUMBERP E1) (= E1 E2))
(T (EQUAL E1 E2))))
;; try and do the right thing for random lossage
;; right now, this can arise from the CHARACTERS function which
;; returns elements as strings in order to preserve CASE
((and (eq (token-type e1) 'string)
(eq (token-type e2) 'symbol))
(string= e1 (string e2)))
((and (eq (token-type e2) 'string)
(eq (token-type e1) 'symbol))
(string= (string e1) e2))
(t nil)))
(DEFUN ROW-EQUAL? (ROW1 ROW2)
(AND (= (LENGTH ROW1) (LENGTH ROW2))
(NOT (LOOP FOR E1 IN ROW1
FOR E2 IN ROW2
UNLESS (ELEMENT-EQUAL? E1 E2)
RETURN T))))
(DEFUN BOX-EQUAL? (BOX1 BOX2)
(LET ((ROWS1 (GET-BOX-ROWS BOX1))
(ROWS2 (GET-BOX-ROWS BOX2)))
(AND (= (LENGTH ROWS1) (LENGTH ROWS2))
(NOT (LOOP FOR ROW1 IN ROWS1
FOR ROW2 IN ROWS2
UNLESS (ROW-EQUAL? ROW1 ROW2)
RETURN T)))))
;; Useful info
(DEFUN GET-BOX-LENGTH-IN-ROWS (BOX-OR-PORT)
(LET ((BOX (BOX-OR-PORT-TARGET BOX-OR-PORT)))
(COND ((port-box? box) (tell (tell box :ports) :length-in-rows))
((BOX? BOX) (TELL BOX :LENGTH-IN-ROWS))
((NUMBERP BOX) 1)
(T (EVBOX-LENGTH-IN-ROWS BOX)))))
(DEFUN GET-BOX-LENGTH-IN-ELEMENTS (BOX-OR-PORT)
(LET ((BOX (BOX-OR-PORT-TARGET BOX-OR-PORT)))
(COND ((BOX? BOX) (LENGTH (TELL BOX :ELEMENTS)))
((NUMBERP BOX) 1)
(T (EVBOX-LENGTH-IN-ELEMENTS BOX)))))
;; Stringiness and stringosity
(DEFUN ROW-STRING (ROW)
(COND ((ROW? ROW) (TELL ROW :TEXT-STRING))
((EVROW? ROW) (EVROW-TEXT-STRING ROW))
(T (FERROR "Can't coerce ~A into a string" ROW))))
(DEFUN TEXT-STRING (BOX-OR-PORT)
(LET ((BOX (BOX-OR-PORT-TARGET BOX-OR-PORT)))
(COND ((EVBOX? BOX) (EVBOX-TEXT-STRING BOX))
((NUMBERP BOX) (FORMAT NIL "~A" BOX))
((BOX? BOX) (TELL BOX :TEXT-STRING))
(T (FERROR "DOn't know how to make a string from ~A" BOX)))))
;; accessors for inner structure
(DEFUN GET-BOX-ROWS (BOX-OR-PORT &OPTIONAL (SPACES? NIL))
"Returns a list of rows which appear as a list of tokens"
(LET ((BOX (BOX-OR-PORT-TARGET BOX-OR-PORT)))
(COND ((AND SPACES? (BOX? BOX))
(MAP-TELL (TELL BOX :ROWS) :ITEMS))
((BOX? BOX)
(MAP-TELL (TELL BOX :ROWS) :ENTRIES))
((NUMBERP BOX)
(NCONS (NCONS BOX)))
((NULL SPACES?)
(EVBOX-ROW-ENTRIES BOX))
(T (EVBOX-ROW-ITEMS BOX)))))
(DEFUN GET-NTH-ROW (N BOX-OR-PORT &OPTIONAL (SPACES? NIL))
(LET ((BOX (BOX-OR-PORT-TARGET BOX-OR-PORT)))
(COND ((AND (NULL SPACES?) (BOX? BOX))
(TELL-CHECK-NIL (TELL BOX :ROW-AT-ROW-NO N) :ENTRIES))
((BOX? BOX)
(TELL-CHECK-NIL (TELL BOX :ROW-AT-ROW-NO N) :ITEMS))
((AND (NUMBERP BOX) (= 0 N)) (NCONS BOX))
((NULL SPACES?)
(EVROW-ENTRIES (GET-NTH-ROW-IN-EVBOX N BOX)))
(T (EVROW-ITEMS (GET-NTH-ROW-IN-EVBOX N BOX))))))
(DEFUN GET-FIRST-ROW (BOX-OR-PORT &OPTIONAL (SPACES? NIL))
(LET ((BOX (BOX-OR-PORT-TARGET BOX-OR-PORT)))
(COND ((AND (NULL SPACES?) (BOX? BOX))
(TELL (TELL BOX :FIRST-INFERIOR-ROW) :ENTRIES))
((BOX? BOX)
(TELL (TELL BOX :FIRST-INFERIOR-ROW) :ITEMS))
((NUMBERP BOX) (NCONS BOX))
((NULL SPACES?)
(EVROW-ENTRIES (GET-FIRST-ROW-IN-EVBOX BOX)))
(T (EVROW-ITEMS (GET-FIRST-ROW-IN-EVBOX BOX))))))
(DEFUN GET-FIRST-ELEMENT (BOX)
(IF (NUMBERP BOX) BOX
(DOTIMES (I (GET-BOX-LENGTH-IN-ROWS BOX))
(LET ((ENTRIES (GET-NTH-ROW I BOX)))
(WHEN (NOT (NULL ENTRIES)) (RETURN (CAR ENTRIES)))))))
(DEFUN GET-BOX-ELEMENTS (BOX)
(COND ((BOX? BOX) (TELL BOX :ELEMENTS))
(T (GET-EVBOX-ELEMENTS BOX))))
;;; This port does not create back-pointers to the port so that the ports can be GC'd after
;;; the evaluation returns
(DEFUN PORT-TO-FOR-EVAL (TARGET &optional name-too)
(LET ((PORT (MAKE-INITIALIZED-BOX :TYPE 'PORT-BOX)))
(TELL PORT :SET-PORT-TO-BOX-FOR-EVAL TARGET)
(when (and name-too (not (null (box-name target))))
(tell port :set-name (make-name-row (list (box-name target)))))
PORT))
;; this should make Evports but STREAMS have to be fixed to handled EvBoxes first...
(DEFSUBST PORT-TO-INFERIORS-IN-LIST (LIST &optional name-too)
(MAPCAR #'(LAMBDA (X) (IF (EVAL-BOX? X) (PORT-TO-FOR-EVAL X name-too) X)) LIST))
(DEFUN PORT-TO-INFERIORS (EVROW)
"Makes an EVROW which replaces every BOX in the arg with a PORT to that BOX."
(MAKE-EVROW-FROM-ITEMS (PORT-TO-INFERIORS-IN-LIST (EVROW-ITEMS EVROW))))
;;; mutators
;; for ROWS (delete, insert and change)
;0 based
(DEFUN DELETE-ROW-AT-ROW-NO (N BOX &OPTIONAL (NEW? NIL))
(COND ((NOT-NULL NEW?)
(LET ((ROWS (GET-BOX-ROWS BOX)))
(MAKE-EVDATA ROWS (APPEND (mapcar #'make-evrow-from-items (FIRSTN N ROWS))
(mapcar #'make-evrow-from-items (NTHCDR (1+ N) ROWS))))))
((BOX? BOX) (TELL BOX :DELETE-ROW-AT-ROW-NO N)
(TELL BOX :MODIFIED)
':NOPRINT)
(T (SETF (EVBOX-ROWS BOX) (APPEND (FIRSTN N (EVBOX-ROWS BOX))
(NTHCDR (1+ N) (EVBOX-ROWS BOX))))
':NOPRINT)))
(DEFUN INSERT-ROW-AT-ROW-NO (N BOX NEW-ROW &OPTIONAL (NEW? NIL))
(COND ((NOT-NULL NEW?)
(LET ((ROWS (GET-BOX-ROWS BOX)))
(MAKE-EVDATA ROWS (APPEND (mapcar #'make-evrow-from-items (FIRSTN N ROWS))
(NCONS NEW-ROW)
(mapcar #'make-evrow-from-items (NTHCDR N ROWS))))))
((BOX? BOX)
(TELL BOX :INSERT-ROW-AT-ROW-NO (MAKE-ROW (evrow-items NEW-ROW)) N)
(TELL BOX :MODIFIED)
':NOPRINT)
(T (SETF (EVBOX-ROWS BOX)
(APPEND (FIRSTN N (EVBOX-ROWS BOX))
(NCONS NEW-ROW)
(NTHCDR N (EVBOX-ROWS BOX))))
':NOPRINT)))
(DEFUN CHANGE-ROW-AT-ROW-NO (N BOX NEW-ROW &OPTIONAL (NEW? NIL))
(COND ((NOT-NULL NEW?)
(LET ((ROWS (GET-BOX-ROWS BOX)))
(MAKE-EVDATA ROWS (APPEND (mapcar #'make-evrow-from-items (FIRSTN N ROWS))
(NCONS NEW-ROW)
(mapcar #'make-evrow-from-items (NTHCDR (1+ N) ROWS))))))
((BOX? BOX)
(TELL BOX :DELETE-ROW-AT-ROW-NO N)
(TELL BOX :INSERT-ROW-AT-ROW-NO (MAKE-ROW (evrow-items NEW-ROW)) N)
(TELL BOX :MODIFIED)
':NOPRINT)
(T (SETF (EVBOX-ROWS BOX)
(APPEND (FIRSTN N (EVBOX-ROWS BOX))
(NCONS NEW-ROW)
(NTHCDR (1+ N) (EVBOX-ROWS BOX))))
':NOPRINT)))
;;; Useful interactions between character level and entry level representation
;;; these SIDE EFFECT
;;; 0 based
;; Since we lose some character information after READing (for example in a LABEL PAIR), we
;; will sometimes need to look directly at the row on a character by character basis
;; these don't as yet handle nesting of compound items accurately e.g. (LABEL-PAIR (UNBOX...))
(DEFSUBST COMPOUND-ENTRY? (ENTRY)
"Returns T if the entry is allowed to have spaces within its visual representation. "
(OR (LABEL-PAIR? ENTRY) (UNBOX-TOKEN? ENTRY) (EVAL-IT-TOKEN? ENTRY)))
(DEFSUBST EXAMINE-ROW-CHARACTERS? (ENTRY)
"Returns T if the row entry's length cannot be determined from the entry itself. "
(OR (COMPOUND-ENTRY? ENTRY) ; spaces within the pair are lost
(FIXP ENTRY))) ; *NOPOINT lossage
(DEFSUBST COMPOUND-ENTRY-PROLOGUE-LENGTH (ENTRY)
(COND ((LABEL-PAIR? ENTRY) (CHA-LENGTH-OF-EVROW-ITEM (LABEL-PAIR-LABEL ENTRY)))
;; assume that it is either an UNBOX or EVAL-IT token
(T 1)))
(DEFSUBST COMPOUND-ENTRY-EPILOGUE-LENGTH (ENTRY)
(COND ((LABEL-PAIR? ENTRY) (CHA-LENGTH-OF-EVROW-ITEM (LABEL-PAIR-ELEMENT ENTRY)))
((UNBOX-TOKEN? ENTRY) (CHA-LENGTH-OF-EVROW-ITEM (UNBOX-TOKEN-ELEMENT ENTRY)))
((EVAL-IT-TOKEN? ENTRY) (CHA-LENGTH-OF-EVROW-ITEM (EVAL-IT-TOKEN-ELEMENT ENTRY)))))
(DEFSUBST COMPOUND-ENTRY-INTERVENING-LENGTH (MID-NO ROW IGNORE-CHAS)
(LOOP FOR CHA-NO = MID-NO THEN (1+ CHA-NO)
FOR CHA = (CHA-CODE (TELL ROW :CHA-AT-CHA-NO CHA-NO))
UNTIL (NOT (MEMBER CHA IGNORE-CHAS))
SUMMING 1))
(DEFSUBST COMPOUND-ENTRY-LENGTH (START-NO ROW ENTRY)
(LET ((PROLOGUE-LENGTH (COMPOUND-ENTRY-PROLOGUE-LENGTH ENTRY)))
(+ PROLOGUE-LENGTH
(COMPOUND-ENTRY-INTERVENING-LENGTH (+ START-NO PROLOGUE-LENGTH)
ROW
(IF (LABEL-PAIR? ENTRY) '(#\SPACE #\:) '(#\SPACE)))
(COMPOUND-ENTRY-EPILOGUE-LENGTH ENTRY))))
(DEFSUBST ACTUAL-CHA-LENGTH-OF-ENTRY (START-NO ROW ENTRY)
(IF (COMPOUND-ENTRY? ENTRY)
(COMPOUND-ENTRY-LENGTH START-NO ROW ENTRY)
(LOOP FOR CHA-NO = START-NO THEN (1+ CHA-NO)
FOR CHA = (TELL ROW :CHA-AT-CHA-NO CHA-NO)
UNTIL (EQUAL CHA #\SPACE)
SUMMING 1)))
(DEFUN GET-CHA-NOS-OF-ENTRY (ROW ENTRY-NO)
"Returns 2 values corresponding to the start and stop CHA-NO of the entry. "
(LOOP WITH ENTRY-INDEX = 0
WITH CHA-NO = 0
FOR ENTRY IN (TELL ROW :EVROW)
WHEN (AND (NOT (SPACES? ENTRY)) (= ENTRY-NO ENTRY-INDEX))
RETURN (VALUES CHA-NO (+ CHA-NO (IF (EXAMINE-ROW-CHARACTERS? ENTRY)
(ACTUAL-CHA-LENGTH-OF-ENTRY CHA-NO ROW ENTRY)
(CHA-LENGTH-OF-EVROW-ITEM ENTRY))))
UNLESS (SPACES? ENTRY)
DO (INCF ENTRY-INDEX)
DO (INCF CHA-NO (IF (EXAMINE-ROW-CHARACTERS? ENTRY)
(ACTUAL-CHA-LENGTH-OF-ENTRY CHA-NO ROW ENTRY)
(CHA-LENGTH-OF-EVROW-ITEM ENTRY)))
FINALLY
(FERROR "There are less than ~D entries in ~A" ENTRY-NO ROW)))
(DEFUN MAKE-ROW-WITH-PADDED-VALUE (THING &OPTIONAL (PAD-LEFT NIL) (PAD-RIGHT T))
(LET ((ROW (MAKE-ROW `(,THING))))
(WHEN PAD-LEFT (TELL ROW :INSERT-CHA-AT-CHA-NO #\SPACE 0))
(WHEN PAD-RIGHT (TELL ROW :APPEND-CHA #\SPACE))
ROW))
;;; Row mutators for
(DEFUN DELETE-ENTRY-IN-ROW-AT-ENTRY-NO (ROW ENTRY-NO)
(MULTIPLE-VALUE-BIND (START-CHA-NO STOP-CHA-NO)
(GET-CHA-NOS-OF-ENTRY ROW ENTRY-NO)
(TELL ROW :DELETE-CHAS-BETWEEN-CHA-NOS START-CHA-NO STOP-CHA-NO)))
(DEFUN INSERT-ENTRY-IN-ROW-AT-ENTRY-NO (ROW ENTRY-NO NEW-ENTRY)
(IF ( ENTRY-NO (LENGTH (TELL ROW :ENTRIES)))
(TELL ROW :INSERT-ROW-CHAS-AT-CHA-NO (MAKE-ROW-WITH-PADDED-VALUE NEW-ENTRY T)
(TELL ROW :LENGTH-IN-CHAS))
(TELL ROW :INSERT-ROW-CHAS-AT-CHA-NO (MAKE-ROW-WITH-PADDED-VALUE NEW-ENTRY)
(GET-CHA-NOS-OF-ENTRY ROW ENTRY-NO))))
(DEFUN CHANGE-ENTRY-IN-ROW-AT-ENTRY-NO (ROW ENTRY-NO NEW-ENTRY)
(LET ((NEW-ROW (MAKE-ROW-WITH-PADDED-VALUE NEW-ENTRY)))
(MULTIPLE-VALUE-BIND (START-CHA-NO STOP-CHA-NO)
(GET-CHA-NOS-OF-ENTRY ROW ENTRY-NO)
(TELL ROW :DELETE-CHAS-BETWEEN-CHA-NOS START-CHA-NO STOP-CHA-NO)
(TELL ROW :INSERT-ROW-CHAS-AT-CHA-NO NEW-ROW START-CHA-NO))))
;;; The actual mutators which other functions can call
;;; No bounds checking
(DEFUN DELETE-ITEM-AT-ITEM-NO-IN-ROW-NO (I R BOX &OPTIONAL (NEW? NIL))
(COND ((NOT-NULL NEW?)
(LET* ((ROWS (GET-BOX-ROWS BOX))
(row (get-nth-row r box)))
(MAKE-EVDATA-FROM-ROWS (APPEND (FIRSTN R ROWS)
(NCONS (append (firstn i row) (nthcdr (1+ i) row)))
(NTHCDR (1+ R) ROWS)))))
((BOX? BOX)
(DELETE-ENTRY-IN-ROW-AT-ENTRY-NO (TELL BOX :ROW-AT-ROW-NO R) I)
':NOPRINT)
(T (DELETE-NTH-ITEM-IN-EVROW I (GET-NTH-ROW-IN-EVBOX R BOX))
':NOPRINT)))
(DEFUN INSERT-ITEM-AT-ITEM-NO-IN-ROW-NO (I R BOX NEW-ITEM &OPTIONAL (NEW? NIL))
(COND ((NOT-NULL NEW?)
(LET* ((ROWS (GET-BOX-ROWS BOX))
(ROW (GET-NTH-ROW R BOX)))
(MAKE-EVDATA-FROM-ROWS (APPEND (FIRSTN R ROWS)
(NCONS (APPEND (FIRSTN I ROW) (NCONS NEW-ITEM)
(NTHCDR I ROW)))
(NTHCDR (1+ R) ROWS)))))
((BOX? BOX)
(INSERT-ENTRY-IN-ROW-AT-ENTRY-NO (TELL BOX :ROW-AT-ROW-NO R) I NEW-ITEM)
':NOPRINT)
(T (INSERT-NTH-ITEM-IN-EVROW I (GET-NTH-ROW-IN-EVBOX R BOX) NEW-ITEM)
':NOPRINT)))
(DEFUN CHANGE-ITEM-AT-ITEM-NO-IN-ROW-NO (I R BOX NEW-ITEM &OPTIONAL (NEW? NIL))
(COND ((NOT-NULL NEW?)
(LET* ((ROWS (GET-BOX-ROWS BOX))
(ROW (GET-NTH-ROW R BOX)))
(MAKE-EVDATA-FROM-ROWS (APPEND (FIRSTN R ROWS)
(NCONS (APPEND (FIRSTN I ROW) (NCONS NEW-ITEM)
(NTHCDR (1+ I) ROW)))
(NTHCDR (1+ R) ROWS)))))
((BOX? BOX)
(CHANGE-ENTRY-IN-ROW-AT-ENTRY-NO (TELL BOX :ROW-AT-ROW-NO R) I NEW-ITEM)
':NOPRINT)
(T (CHANGE-NTH-ITEM-IN-EVROW I (GET-NTH-ROW-IN-EVBOX R BOX) NEW-ITEM)
':NOPRINT)))
(DEFUN GET-ROW-AND-COL-NUMBER (N BOX)
"Converts 1-based GET-NTH coordinates into 0-based GET-RC coordinates. Values returned are row number and column number"
(DECLARE (VALUES ROW-NO INDEX))
(LOOP WITH INDEX = (1- N)
FOR ROW IN (GET-BOX-ROWS BOX)
FOR ROW-NO = 0 THEN (1+ ROW-NO)
FOR LENGTH = (LENGTH ROW)
WHEN (< INDEX LENGTH)
RETURN (VALUES ROW-NO INDEX)
DO (SETQ INDEX (- INDEX LENGTH))))
;;; gets the whitespace out (you try scrubbing them out....)
(DEFUN TRIM-EMPTY-ROWS (LIST-OF-ROWS)
(LOOP FOR ROW IN LIST-OF-ROWS
UNLESS (NULL (SUBSET-NOT #'SPACES? (EVROW-ITEMS ROW)))
COLLECT ROW INTO NEW-ROWS
FINALLY
(RETURN (IF (NULL NEW-ROWS) `(,(MAKE-EMPTY-EVROW)) NEW-ROWS))))